背景
借助阮一峰老师的话:它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。
在Javascript中,this指向函数执行时的当前对象
这句话看似平常,可是要非常注意三个字:" 运行时 ",这说明 this 关键字只与函数的执行环境有关,而与声明环境没有关系。也就是这个 this 到底代表的是什么对象要等到函数运行时才知道,有点类似函数定义时的参数列表只在函数调用时才传入真正的对象
this 关键字虽然会根据环境变化,但是它始终代表的是调用当前函数的那个对象。
这就引出了 JS 中函数调用的问题。
在 JS 中调用函数的模式可以分为 4 种:
- 方法调用模式
- 函数调用模式
- 构造器调用模式
- apply 调用模式
方法调用模式
当函数被保存为一个对象的属性时,它就可称为这个对象的方法。当一个方法被调用时,this 被绑定到这个对象上。如果调用表达式包含一个提取属性的动作(. 或 []),那么它被称为方法调用
var name = "window";
var obj = {
name: "kxy",
sayName: function() {
console.log(this.name);
}
};
obj.sayName(); //kxysayName 函数作为对象 obj 的方法调用,所以函数体中的 this 就代表 obj 对象
调用的先后顺序
对象的属性调用和函数的方法调用
- new Foo.getName(),这里等价于 new (Foo.getName()),先执行 Foo.getName(),输出 2,然后 new 一个实例;
- new Foo().getName(),这里等价于 (new Foo()).getName(), 先 new 一个 Foo 的实例,再执行这个实例的 getName 方法,但是这个实例本身没有这个方法,所以去原型链 protot 上边找,
实例.__protot__ === Foo.prototype,所以输出 3; - new new Foo().getName(),这里等价于 new (new Foo().getName()),如上述 6,先输出 3,然后 new 一个 new Foo().getName() 的实例
函数调用模式
当一个函数并非一个对象的属性时,那么它就是被当做函数来调用的。在此种模式下,this 被绑定为全局对象,在浏览器环境下就是 window 对象。例如:
var name = "window";
function sayName() {
console.log(this.name);
}
sayName();sayName 以函数调用模式调用,所以函数体中的 this 代表 window 对象
构造函数模式
如果在一个函数前面加上 new 关键字来调用,那么就会创建一个连接到该函数的 prototype 成员的新对象,同时,this 会被绑定到这个新对象上。这种情况下,这个函数就可以成为此对象的构造函数。
function Obj() {
this.name = "kxy";
}
var person = new Obj();
console.log(person.name); //kxyObj 作为构造函数被调用,函数体内的 this 被绑定为新创建的对象 person
apply 调用模式
在 JS 中,函数也是对象,所有函数对象都有两个方法:apply 和 call,这两个方法可以让我们构建一个参数数组传递给调用函数,也允许我们改变 this 的值
var name = "window";
var person = {
name: "kxy"
};
function sayName() {
console.log(this.name);
}
sayName(); //window
sayName.apply(person); //kxy
sayName.apply(); //window当以函数调用模式调用 sayName 时,this 代表 window;当用 apply 模式调用 sayName,并给它传入的第一个参数为 person 时,this 被绑定到 person 对象上。如果不给 apply 传入任何参数,则 this 代表 window
demo 测验
var name = "window";
function showName() {
console.log(this.name);
}
var person1 = {
name: "kxy",
sayName: showName
}
var person2 = {
name: "Jake",
sayName: function() {
var fun = person1.sayName;
fun();
}
}
person1.sayName(); //kxy
person2.sayName(); //window先看第一个执行语句:person1.sayName(); 首先确定这是方法调用模式,对象为 person1,再看 sayName 被赋值为全局函数对象 showName,在 showName 执行时,this 绑定的是 person1,所以结果为 "kxy"。
再看第二个执行语句:person2.sayName(); 这还是方法调用模式,对象为 person2,调用的是它的 sayName 方法。再看 sayName 函数体,发现函数体最终执行的函数是 fun,fun 是用函数调用模式调用的。而 fun 最终也被赋值为 showName 函数,因为 fun 是用函数调用模式调用的,所以这里的 this 绑定为 window,结果为 "window"
总结
函数 调用 和 执行 不同,调用作为对象的方法使用,执行则不强调作为对象的方法来说,一般是 window 对象
全局作用域中,this 指向 window
在普通函数中,谁调用函数,this 指向谁
箭头函数中没有自己的 this,它指向其父级所处的上下文
事件绑定中的 this,指向事件源
// 事件源.onclik = function(){ } //this -> 事件源
// 事件源.addEventListener(function(){ }) //this->事件源
var div = document.querySelector('div');
div.addEventListener('click',function() {
console.log(this); //this->div
});
div.onclick = function() {
console.log(this) //this->div
}定时器中采用
回调函数作为处理函数,所以回调函数中的 this 指向 window
构造函数中的 this 指向实例化对象
obt.fn.call(),这里 call 的参数啥都没写,就表示 null,如果 call 的参数为 undefined 或 null,那么 this 就会指向全局对象
obt.fn();
obt.fn.call();
(obt.fn)(); // 加括号改变运算顺序
匿名函数的 this 是指向全局对象的
IIFE(立即调用函数表达式,别名叫匿名函数自调用)中的this指向 window